import numpy as np클래스공부 4단계
Motivating Example
- 가위바위보
- 방법1
class RPC2:
def throw2(self):
print(np.random.choice(['가위', '바위','보']))a=RPC2()a.throw2()바위
- 방법2
class RPC:
def thorw(self, candidate):
print(np.random.choice(candidate))a=RPC()a.thorw(['가위','바위','보'])바위
- 방법3
class RPC3:
def __init__(self, candidate=['가위','바위','보']):
self.candidate = candidate
def throw3(self):
print(np.random.choice(self.candidate))a=RPC3() # __init__ 는 암묵적으로 실행a.throw3()바위
- 방법4
class RPC4:
passb=RPC4()def initt(b, candidate=['가위','바위','보']):
b.candidate = candidateinitt(b)b.candidate['가위', '바위', '보']
def throww(b):
print(np.random.choice(b.candidate))throww(b)가위
- 방법5
# 위의 코드를 하나로 합치면..class RPC4:
def __init__(self, candidate=['가위','바위','보']):
self.candidate = candidate
def throww(self):
print(np.random.choice(self.candidate))a=RPC4()a.throww()가위
- 생각해보니까 throw는 choose + show 의 결합인 것 같다.
class RPC:
def __init__(self, candidate=['가위','바위','보']):
self.candidate = candidate
def choose(self):
self.actions = np.random.choice(self.candidate)
def show(self):
print(self.actions)a=RPC()a.actions() # 지금은 정의되지 않음AttributeError: 'RPC' object has no attribute 'actions'
a.choose()a.actions # 가위, 바위, 보 중 고른 결과가 나옴'가위'
a.show()가위
보충학습 : 위와 같은 코드
class _RPS: ## 시점1
pass # <- 이렇게하면 아무기능이 없는 비어있는 클래스가 정의된다_a = _RPS() ## 시점2
def _init(_a,candidate=['가위','바위','보']):
_a.candidate = candidate
_init(_a)_a.actions ## 시점3AttributeError: '_RPS' object has no attribute 'actions'
def _choose(_a): ## 시점4
_a.actions = np.random.choice(_a.candidate)
_choose(_a)_a.actions ## 시점5'보'
def _show(_a): ## 시점6
print(_a.actions)
_show(_a)보
- 또 다른 인스턴스 b를 만들자. b는 가위만 낼 수 있다.
RPC?Init signature: RPC(candidate=['가위', '바위', '보']) Docstring: <no docstring> Type: type Subclasses:
b=RPC(['가위'])b.candidate['가위']
b.choose()
b.show()가위
- a,b의 선택들을 모아서 기록을 하고 싶다.
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1]) # 지금 현재 내가 선택한 마지막만 보여줘!a=RPS()
b=RPS(['가위'])for i in range(5):
a.choose()
a.show()바위
가위
바위
바위
보
a.actions # 지금까지 뽑힌 히스토리들['바위', '가위', '바위', '바위', '보']
for i in range(5):
b.choose()
b.show()가위
가위
가위
가위
가위
b.actions['가위', '가위', '가위', '가위', '가위']
a.candidate, a.actions(['가위', '바위', '보'], ['바위', '가위', '바위', '바위', '보'])
b.candidate, b.actions(['가위'], ['가위', '가위', '가위', '가위', '가위'])
- info라는 함수를 만들어서 a의 오브젝트가 가지고 있는 정보를 모두 보도록 하자.
(예비학습) 문자열 \n이 포함된다면?
'asdf\n1234''asdf\n1234'
print('asdf\n1234')asdf
1234
예비학습 끝
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def info(self):
print("낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions))a=RPS()
b=RPS(['가위'])for i in range(5):
a.choose()
a.show() 가위
바위
가위
보
보
for i in range(5):
b.choose()
b.show()가위
가위
가위
가위
가위
a.info()낼 수 있는 패: ['가위', '바위', '보']
기록: ['가위', '바위', '가위', '보', '보']
b.info()낼 수 있는 패: ['가위']
기록: ['가위', '가위', '가위', '가위', '가위']
- 만들고 보니까 info와 print의 기능이 거의 비슷함 \(\to\) print(a)를 하면 a.info()와 동일한 효과를 내도록 만들 수 있을까?
- 안될 거 같다. 왜?
안될 것 같은 이유 1: print 는 파이썬 내장기능, 내장기능을 우리가 맘대로 커스터마이징해서 쓰기는 어려울 것 같다.
안될 것 같은 이유 2: 이유1이 해결된다고 해도 문제다. 다 꼬아져버려… 그럼 지금까지 사용했던 print()의 결과는 어떻게 되는가?
(예)
type(a)__main__.RPS
a?Type: RPS String form: <__main__.RPS object at 0x7f6565f29e10> Docstring: <no docstring>
- 그런데 a의 자료형(RPS자료형)에 해당하는 오브젝트들에 한정하여 print를 수정하는 방법이 가능하다면? (그럼 다른 오브젝트들은 수정된 print에 영향을 받지 않음)
__str___
- 관찰1: 현재 print(a)의 결과는 아래와 같다.
print(a)<__main__.RPS object at 0x7f6565f29e10>
print([1,2,3])[1, 2, 3]
- a는 RPS클래스에서 만든 오브젝트이며 a가 저장된 메모리 주소는 0x7f6565f29e10라는 의미
- 관찰2: a에는 __str__ 이 있다.
dir(a) # a + _ + tab을 누르면 숨겨진 메소드들이 나온다.['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'actions',
'candidate',
'choose',
'info',
'show']
set(dir(a)) & {'__str__'}{'__str__'}
a.__str__<method-wrapper '__str__' of RPS object at 0x7f6565f29e10>
이것을 함수처럼 사용하니까 아래와 같이 된다.
a.__str__()'<__main__.RPS object at 0x7f6565f29e10>'
?? print(a)를 해서 나오는 문자열이 리턴된다..
print(a.__str__()) # 이거 print(a)를 실행한 결과와 같다?<__main__.RPS object at 0x7f6565f29e10>
- 생각: 만약 내가 a.__str__() 라는 함수를 재정의하여 리턴값을 boram hahaha로 바꾸게 되면 print(a)해서 나오는 결과는 어떻게 될까? (해커???)
(예비학습)
def f():
print('adsf')f()adsf
def f():
print('boram hahaha')f()boram hahaha
이런식으로 함수가 이미 정의되어 있더라도, 내가 나중에 덮어씌우면 그 함수의 기능을 다시 정의한다.
(해킹시작)
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __str__(self):
return 'boram hahaha'
def info(self):
print("낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions))a=RPS()print(a)boram hahaha
print(a.__str__())boram hahaha
# 다른건 다 변함이 없음a.choose()
a.show()가위
a.actions['가위']
a.info()낼 수 있는 패: ['가위', '바위', '보']
기록: ['가위']
- __str__의 리턴값을 info에서 타이핑했던 문자열로 재정의 한다면?
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __str__(self):
return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)a=RPS()print(a)낼 수 있는 패: ['가위', '바위', '보']
기록: []
a.choose()
a.show()바위
print(a)낼 수 있는 패: ['가위', '바위', '보']
기록: ['보', '바위']
파이썬의 비밀2
- print(a)와 print(a.__str__()) 는 같은 문법이다.
- 참고로 a.__Str__() 와 str(a) 도 같은 문법
a.__str__()"낼 수 있는 패: ['가위', '바위', '보']\n기록: ['보', '바위']"
str(a)"낼 수 있는 패: ['가위', '바위', '보']\n기록: ['보', '바위']"
- 지금까지 썼던 기능들 확인!
(예제1)
a=[1,2,3]
print(a)[1, 2, 3]
a.__str__()'[1, 2, 3]'
str(a)'[1, 2, 3]'
(예제2)
a={1,2,3}
print(a){1, 2, 3}
a.__str__()'{1, 2, 3}'
str(a)'{1, 2, 3}'
(예제3)
a=np.array(1)
a.shape()
type(a.shape)tuple
print(a.shape)()
a.shape.__str__()'()'
str(a.shape)'()'
(예제4)
a = range(10)
print(a)range(0, 10)
a.__str__()'range(0, 10)'
(예제5)
a = np.arange(100).reshape(10,10)
print(a)[[ 0 1 2 3 4 5 6 7 8 9]
[10 11 12 13 14 15 16 17 18 19]
[20 21 22 23 24 25 26 27 28 29]
[30 31 32 33 34 35 36 37 38 39]
[40 41 42 43 44 45 46 47 48 49]
[50 51 52 53 54 55 56 57 58 59]
[60 61 62 63 64 65 66 67 68 69]
[70 71 72 73 74 75 76 77 78 79]
[80 81 82 83 84 85 86 87 88 89]
[90 91 92 93 94 95 96 97 98 99]]
a.__str__()'[[ 0 1 2 3 4 5 6 7 8 9]\n [10 11 12 13 14 15 16 17 18 19]\n [20 21 22 23 24 25 26 27 28 29]\n [30 31 32 33 34 35 36 37 38 39]\n [40 41 42 43 44 45 46 47 48 49]\n [50 51 52 53 54 55 56 57 58 59]\n [60 61 62 63 64 65 66 67 68 69]\n [70 71 72 73 74 75 76 77 78 79]\n [80 81 82 83 84 85 86 87 88 89]\n [90 91 92 93 94 95 96 97 98 99]]'
str(a)'[[ 0 1 2 3 4 5 6 7 8 9]\n [10 11 12 13 14 15 16 17 18 19]\n [20 21 22 23 24 25 26 27 28 29]\n [30 31 32 33 34 35 36 37 38 39]\n [40 41 42 43 44 45 46 47 48 49]\n [50 51 52 53 54 55 56 57 58 59]\n [60 61 62 63 64 65 66 67 68 69]\n [70 71 72 73 74 75 76 77 78 79]\n [80 81 82 83 84 85 86 87 88 89]\n [90 91 92 93 94 95 96 97 98 99]]'
__repr__
- 생각해보니까 print를 해서 원하는 정보를 확인하는 건 아니었음
a=[1,2,3]a[1, 2, 3]
print(a) # print(a.__str__()) + enter => a + enter 와 같은 효과?[1, 2, 3]
- a + 엔터를 하면 print(a) + 엔터를 하는 것과 같은 효과인가?
(반례)
a=np.array([1,2,3,4]).reshape(2,2)aarray([[1, 2],
[3, 4]])
print(a)[[1 2]
[3 4]]
- a + 엔터를 하면 print(a) + 엔터가 다른 경우도 있다. \(\to\) 서로 다른 숨겨진 기능이 잇다! \(\to\) 결론 : 그 기능은 __repr__에 저장되어 있다.
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __repr__(self):
return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)a=RPS()a # print(a.__repr__())낼 수 있는 패: ['가위', '바위', '보']
기록: []
- 그럼 지금까지 한것은?
a = np.array([1,2,3])aarray([1, 2, 3])
print(a)[1 2 3]
a.__repr__()'array([1, 2, 3])'
a.__str__()'[1 2 3]'
파이썬의 비밀3
- 대화형 콘솔에서 오브젝트 이름 + 엔터 를 쳐서 나오는 출력은 __repr__ 의 결과와 연관 있다.
a = np.array(range(10000)).reshape(100,100)aarray([[ 0, 1, 2, ..., 97, 98, 99],
[ 100, 101, 102, ..., 197, 198, 199],
[ 200, 201, 202, ..., 297, 298, 299],
...,
[9700, 9701, 9702, ..., 9797, 9798, 9799],
[9800, 9801, 9802, ..., 9897, 9898, 9899],
[9900, 9901, 9902, ..., 9997, 9998, 9999]])
a.__repr__()'array([[ 0, 1, 2, ..., 97, 98, 99],\n [ 100, 101, 102, ..., 197, 198, 199],\n [ 200, 201, 202, ..., 297, 298, 299],\n ...,\n [9700, 9701, 9702, ..., 9797, 9798, 9799],\n [9800, 9801, 9802, ..., 9897, 9898, 9899],\n [9900, 9901, 9902, ..., 9997, 9998, 9999]])'
- 참고로 a.__repr__() 은 representation의 약자인데, repr(a)와 같다.
주피터 노브북의 비밀 (__repr__html__)
- 요즘에는 IDE의 발전에 따라서 오브젝트이름+엔터 칠때 나오는 출력의 형태도 다양해지고 있다.
import pandas as pddf = pd.DataFrame({'a':[1,2,3],'b':[2,3,4]})df| a | b | |
|---|---|---|
| 0 | 1 | 2 |
| 1 | 2 | 3 |
| 2 | 3 | 4 |
- 예쁘게 나온다.
- 그런데? print(df.__repr__())의 결과가 조금 다르게 나온당
print(df.__repr__()) a b
0 1 2
1 2 3
2 3 4
- print(df.__repr__()) 는 예전 검은화면에서 코딩할 때가 나오는 출력임
Python 3.10.2 | packaged by conda-forge | (main, Feb 1 2022, 19:28:35) [GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
> >> import pandas as pd
>>> df = pd.DataFrame({'a':[1,2,3],'b':[2,3,4]})>>> df
a b
0 1 2
1 2 3
2 3 4
>>>- 주피터에서는? “오브젝트이름+엔터”치면 HTML(df._repr_html_())이 실행되고, _repr_html_()이 정의되어 있지 않으면 print(df.__repr__())이 실행된다.
df._repr_html_()'<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border="1" class="dataframe">\n <thead>\n <tr style="text-align: right;">\n <th></th>\n <th>a</th>\n <th>b</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>1</td>\n <td>2</td>\n </tr>\n <tr>\n <th>1</th>\n <td>2</td>\n <td>3</td>\n </tr>\n <tr>\n <th>2</th>\n <td>3</td>\n <td>4</td>\n </tr>\n </tbody>\n</table>\n</div>'
- html코드
from IPython.core.display import HTMLHTML('<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border="1" class="dataframe">\n <thead>\n <tr style="text-align: right;">\n <th></th>\n <th>a</th>\n <th>b</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>1</td>\n <td>2</td>\n </tr>\n <tr>\n <th>1</th>\n <td>2</td>\n <td>3</td>\n </tr>\n <tr>\n <th>2</th>\n <td>3</td>\n <td>4</td>\n </tr>\n </tbody>\n</table>\n</div>')| a | b | |
|---|---|---|
| 0 | 1 | 2 |
| 1 | 2 | 3 |
| 2 | 3 | 4 |
HTML(df._repr_html_())| a | b | |
|---|---|---|
| 0 | 1 | 2 |
| 1 | 2 | 3 |
| 2 | 3 | 4 |
- 물론 df._repr_html_() 함수가 내부적으로 있어도 html이 지원되지 않는 환경이라면 print(__repr__())이 내부적으로 수행된다.
__repr__와 __str__의 우선적용 순위
(예제1)
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __repr__(self):
return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)a=RPS()
a낼 수 있는 패: ['가위', '바위', '보']
기록: []
a.__repr__()"낼 수 있는 패: ['가위', '바위', '보']\n기록: []"
repr(a)"낼 수 있는 패: ['가위', '바위', '보']\n기록: []"
- 여기까지는 상식수준의 결과. 아래 관찰하자
print(a) # print(a.__str__())낼 수 있는 패: ['가위', '바위', '보']
기록: []
a.__str__()"낼 수 있는 패: ['가위', '바위', '보']\n기록: []"
str(a)"낼 수 있는 패: ['가위', '바위', '보']\n기록: []"
- __str__()은 건드린적이 없다…?
a.__repr__??Signature: a.__repr__() Docstring: Return repr(self). Source: def __repr__(self): return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions) File: ~/Dropbox/coco/posts/python/<ipython-input-201-29baf6ff56bf> Type: method
얘는 건드림
a.__str__??Signature: a.__str__() Call signature: a.__str__(*args, **kwargs) Type: method-wrapper String form: <method-wrapper '__str__' of RPS object at 0x7f6561614c10> Docstring: Return str(self).
얘는 안건드렸는디..
(예제2)
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __str__(self):
return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)a=RPS()print(a)낼 수 있는 패: ['가위', '바위', '보']
기록: []
a<__main__.RPS at 0x7f6561429950>
a.__str__()"낼 수 있는 패: ['가위', '바위', '보']\n기록: []"
a.__repr__()'<__main__.RPS object at 0x7f6561429950>'
a.__str__??Signature: a.__str__() Docstring: Return str(self). Source: def __str__(self): return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions) File: ~/Dropbox/coco/posts/python/<ipython-input-214-cd2a21868510> Type: method
a.__repr_??Object `a.__repr_` not found.
(예제3)
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __repr__(self):
return "haha"
def __str__(self):
return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)a=RPS()ahaha
print(a)낼 수 있는 패: ['가위', '바위', '보']
기록: []
- __str__ 와 __repr__ 을 건드리지 않고 출력결과를 바꾸고 싶다면?
class RPS:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def _repr_html_(self):
html_str = """
낼 수 있는 패: {} <br/>
기록: {}
"""
return html_str.format(self.candidate,self.actions)a=RPS()a기록: []
print(a)<__main__.RPS object at 0x7f6561534410>
str(a)'<__main__.RPS object at 0x7f6561534410>'
repr(a)'<__main__.RPS object at 0x7f6561534410>'
for i in range(5):
a.choose()
a.show()바위
바위
바위
가위
보
a기록: ['바위', '바위', '바위', '가위', '보']